package cn.edu.nju.ws.sview.views;

import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.Date;
import java.util.HashMap;
import java.util.Properties;

import cn.edu.nju.ws.sview.database.DBConnectionFactory;
import cn.edu.nju.ws.sview.views.listener.AuthoriyRecoveryTask;

/**
 * 控制软件制品的编辑
 * @author Saisai Gong
 *
 */
public class Collaboration {
	public final static String VIEW;
	public final static String DM;
	public final static String ENTITY;
	public final static String DATASET;
	public final static String RELATION;
    private HashMap<Product,Object> productLocks;
    private HashMap<GroupProduct,Object> groupProductLocks;
    
    private Object lockFetchProductLock = null;
	
	static{
		 Properties properties = new Properties();
	   	 try {
				properties.load(Collaboration.class.getClassLoader().getResourceAsStream("conf.properties")) ;
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			VIEW= properties.getProperty("collate_view");
			DM= properties.getProperty("collate_dm");
			ENTITY= properties.getProperty("collate_entity");
			DATASET= properties.getProperty("collate_dataset");
			RELATION= properties.getProperty("collate_relation");
			properties.clear();
	}
	
    private static Collaboration INSTANCE = null;
     
 	private Collaboration() {
 		productLocks = new HashMap<Product, Object>();
 		groupProductLocks = new HashMap<GroupProduct, Object>();
 		lockFetchProductLock = new  Object();
	}
 	
 	/**
 	 * get the product lock(view,entity,dm) and use it to lock product authority access
 	 * @param id. product id
 	 * @return
 	 */
 	private Object getProductLock(String type,int id){
 		Object object = null;
 		Product  product = new Product(type,id);
 		
 		synchronized (lockFetchProductLock) {
			if(productLocks.containsKey(product)){
				object = productLocks.get(product);
			}else{
				Object obj = new Object();
				productLocks.put(product, obj);
				object = obj;
			}
		}
 		
 		return object;
 	}
 	
    /**
     * 得到编辑组某种软件制品的锁用于重名判断等
     * @param type
     * @param groupID
     * @return
     */
 	public Object getGroupProductLock(String type,int groupID){
 		Object object = null;
 		GroupProduct  product = new GroupProduct(groupID,type);
 		
 		synchronized (lockFetchProductLock) {
			if(groupProductLocks.containsKey(product)){
				object = groupProductLocks.get(product);
			}else{
				Object obj = new Object();
				groupProductLocks.put(product, obj);
				object = obj;
			}
		}
 		
 		return object;
 	}
 	
	public static synchronized Collaboration getInstance() {
		if (INSTANCE == null) {
			INSTANCE = new Collaboration();
		}
		return INSTANCE;
	}
	
	/**
	 * 得到软件制品编辑权限
	 * @param type. software product
	 * @param id
	 * @param userID. the userID must be member of group of the product(It is guaranteed by programmers)
	 * @return
	 */
	public boolean getEditAuthority(String type,int id,int userID){
		boolean hasAuthority = false;
		Object authorLock = this.getProductLock(type,id);
		synchronized (authorLock) {
			Connection con = DBConnectionFactory.getConnection();
			try {
				PreparedStatement stmt = con.prepareStatement("select user_id from collaboration where type=? and id=?");
				PreparedStatement newAuthorStmt = con.prepareStatement("insert into collaboration(type,id,user_id,expire_time) values(?,?,?,?)");
				stmt.setString(1, type);
				stmt.setInt(2, id);
				ResultSet rSet = stmt.executeQuery();
				if(rSet.next()){
					int user_id = rSet.getInt("user_id");
					if(userID!=user_id){
						hasAuthority = false;
					}else{
						hasAuthority = true;
					}
				}else{
					newAuthorStmt.setString(1, type);
					newAuthorStmt.setInt(2, id);
					newAuthorStmt.setInt(3, userID);
					long now = new Date().getTime();
					now = now+(AuthoriyRecoveryTask.RECOVERY_MINUTE*60*1000);
					newAuthorStmt.setTimestamp(4, new java.sql.Timestamp(now));
					newAuthorStmt.executeUpdate();
					hasAuthority = true;
				}
				rSet.close();
				stmt.close();
				newAuthorStmt.close();
				con.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				hasAuthority = false;
				e.printStackTrace();
			}
		}
		return hasAuthority;
	}
	
	/**
	 * 查看用户已经获得的编辑权限是否已经过期
	 * @param type. 软件制品类型
	 * @param id
	 * @param userID. the userID must be member of group of the product(It is guaranteed by programmers)
	 * @return
	 */
	public boolean editAuthorityExpire(String type,int id,int userID){
		boolean expire = true;
		Object authorLock = this.getProductLock(type,id);
		synchronized (authorLock) {
			Connection con = DBConnectionFactory.getConnection();
			try {
				PreparedStatement stmt = con.prepareStatement("select expire_time from collaboration where type=? and id=? and user_id=?");
				stmt.setString(1, type);
				stmt.setInt(2, id);
				stmt.setInt(3, userID);
				ResultSet rSet = stmt.executeQuery();
				if(rSet.next()){
					Timestamp time = rSet.getTimestamp("expire_time");
				    Date nowDate =new Date();
				    if(!time.before(nowDate)){
				    	expire = false;
				    }
				}
				rSet.close();
				stmt.close();
				con.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				expire = true;
				e.printStackTrace();
			}
		}
		return expire;
	}
	
	/**
	 * 回收软件制品权限
	 * @param type
	 * @param id
	 * @return if recover successfully
	 */
	public boolean recoverAuthority(String type,int id){
		boolean issuccess = true;
		Object authorLock = this.getProductLock(type,id);
		synchronized (authorLock) {
			Connection con = DBConnectionFactory.getConnection();
			try {
				PreparedStatement stmt = con.prepareStatement("delete from collaboration where type=? and id=?");
				stmt.setString(1, type);
				stmt.setInt(2, id);
				stmt.executeUpdate();
				stmt.close();
				con.close();
				issuccess = false;
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				issuccess = false;
				e.printStackTrace();
			}
		}
		return issuccess;
	}
	
	private static class GroupProduct{
		private int groupID;
		private String type;
		public GroupProduct(int groupID,String type){
			this.type = type;
			this.groupID = groupID;
		}
		@Override
		public int hashCode() {
			final int prime = 31;
			int result = 1;
			result = prime * result + groupID;
			result = prime * result + ((type == null) ? 0 : type.hashCode());
			return result;
		}
		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			GroupProduct other = (GroupProduct) obj;
			if (groupID != other.groupID)
				return false;
			if (type == null) {
				if (other.type != null)
					return false;
			} else if (!type.equals(other.type))
				return false;
			return true;
		}
		
	}
	
	private static class Product{
		private String type;
		private int id;
		public Product(String type,int id){
			this.type = type;
			this.id = id;
		}
		@Override
		public int hashCode() {
			final int prime = 31;
			int result = 1;
			result = prime * result + id;
			result = prime * result + ((type == null) ? 0 : type.hashCode());
			return result;
		}
		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			Product other = (Product) obj;
			if (id != other.id)
				return false;
			if (type == null) {
				if (other.type != null)
					return false;
			} else if (!type.equals(other.type))
				return false;
			return true;
		}
		
	}
	public static void main(String[] args) {
		Collaboration.getInstance().getEditAuthority(ENTITY, 19, 1);
	}
}
